using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.MultiTenancy;
using Abp.Runtime.Security;
using Abp.UI;
using Yozeev.Authentication.External;
using Yozeev.Authentication.JwtBearer;
using Yozeev.Authorization;
using Yozeev.Models.TokenAuth;
using Yozeev.MultiTenancy;
using Yozeev.UserManagerment.Users;
using Abp.Runtime.Caching;
using Yozeev.Authorization.Impersonation;
using Abp;
using Abp.Domain.Uow;
using Abp.IdentityFramework;
using Abp.Json;
using L._52ABP.Common.Consts;
using Yozeev.Authentication.External.ExternalAuth;
using Yozeev.Authentication.External.ExternalAuth.Dto;
using Yozeev.DataFileObjects;
using Yozeev.DataFileObjects.DataTempCache;
using Yozeev.Security.Captcha;
using Yozeev.UserManagerment.Users.UserLink;
using Abp.Extensions;
using L._52ABP.Common.Net.MimeTypes;
using L._52ABP.Core.VerificationCodeStore;
using Yozeev.BusinessLogic.DomainService;
using Yozeev.Models;
using Yozeev.BusinessLogic;
using Yozeev.EntityFrameworkCore;
using Yozeev.SystemConfig.BasicConfig.DomainService;
using Abp.Domain.Entities.Auditing;
using Yozeev.Help;
using Abp.Web.Models;
using System.Web;
using Yozeev.BusinessLogic.Dtos;
using Abp.AutoMapper;

namespace Yozeev.Controllers
{
    [Route("api/[controller]/[action]")]
    public class CheckTicketController : CommonController//YozeevControllerBase
    {
        private readonly ITenantCache _tenantCache;
        private readonly ICacheManager _cacheManager;
        private readonly IImpersonationManager _impersonationManager;
        private readonly IUserLinkManager _userLinkManager;
        private readonly IdentityOptions _identityOptions;
        private readonly IDataTempFileCacheManager _dataTempFileCacheManager;
        private readonly IDataFileObjectManager _dataFileObjectManager;
        private readonly AccountManager _accountManager;
        private readonly ActivityManager _activityManager;
        private readonly DeviceManager _deviceManager;
        private readonly GateRecordManager _gateRecordManager;
        private readonly TicketDetailManager _ticketDetaildManager;
        private readonly TicketDetailHistoryManager _ticketDetailHistorydManager;
        //private readonly SourceManager _sourceManager;


        public CheckTicketController(
            ITenantCache tenantCache,
            ICacheManager cacheManager,
            IImpersonationManager impersonationManager,
            IUserLinkManager userLinkManager,
            IOptions<IdentityOptions> identityOptions,
            UserManager userManager, 
            IDataFileObjectManager dataFileObjectManager, 
            IDataTempFileCacheManager dataTempFileCacheManager
            , AccountManager accountManage
            , SourceManager sourceManager
            , ScheduleManager scheduleManager
            , PayMethodManager payMethodManager
            , TicketPriceManager ticketPriceManager
            , ActivityManager activityManager
            , CustomerManager customerManager
            , DeviceManager deviceManager
            , GateRecordManager gateRecordManager
            , TicketDetailManager ticketDetaildManager
            , TicketDetailHistoryManager ticketDetailHistorydManager
            , TravelAgencyManager travelManager
            ) :base(sourceManager, scheduleManager, payMethodManager, cacheManager, ticketPriceManager, customerManager, userManager, travelManager)
        {
            _tenantCache = tenantCache;
            _cacheManager = cacheManager;
            _impersonationManager = impersonationManager;
            _userLinkManager = userLinkManager;
            _identityOptions = identityOptions.Value;
            _dataFileObjectManager = dataFileObjectManager;
            _dataTempFileCacheManager = dataTempFileCacheManager;
            _accountManager = accountManage;
            _activityManager = activityManager;
            _deviceManager = deviceManager;
            _gateRecordManager = gateRecordManager;
            _ticketDetaildManager = ticketDetaildManager;
            _ticketDetailHistorydManager = ticketDetailHistorydManager;
        }

        #region 卡验票=============================
        /// <summary>
        /// 卡验票
        /// </summary>
        /// <param name="gateNumber">设备号</param>
        /// <param name="jqmpass">卡号</param>
        /// <param name="rdindex">串口号</param>
        /// <returns>{ status = 0，无效，1进方向，3出方向, msg = "验票信息", count = 0 验票成功数量，一般为1,audio="crp.wav" 音频文件名, }</returns>
        [HttpGet,HttpPost]
        [WrapResult(WrapOnSuccess = false, WrapOnError = false)]
        public async Task<CheckResult> CardOpen(string gateNumber, string jqmpass, string rdindex)
        {
            VerifiableTypeEnum verifiableType = VerifiableTypeEnum.IdentityCard;
            CheckResult result = new CheckResult()
            {
                status = 0,
                msg = "刷卡成功",
                count = 1,
            };
            ShowResult showResult = new ShowResult();
            DeviceListDto deviceListDto = new DeviceListDto();
            try
            {
                //从缓存中查询验票信息
                CheckInfo checkInfo = GetCheckInfoCache(gateNumber, jqmpass, rdindex);
                if (checkInfo != null)
                    return checkInfo.checkResult;
                //查找验票设备
                deviceListDto = GetCheckDevice(gateNumber);
                //查询票据
                TicketDetail ticketDetail = GetTicketDetailByCard(jqmpass);
                try
                {
                    //校验票据信息
                    CheckTicketDetail(ticketDetail);
                    //校验航班信息
                    CheckSchedule(ticketDetail.Schedule);
                    result.audio = ticketDetail.ActivityDetail.TicketPrice.Ticket.AudioName;
                    showResult = getUrlByItem(ticketDetail, true, "验票成功");
                }
                catch (Exception ex)
                {
                    result.status = 0;
                    result.msg = ex.Message;
                    result.count = 0;
                    result.audio = "ypsb.wav";
                    showResult = getUrlByItem(ticketDetail, false, ex.Message);
                }
                //赋值输出结果
                result.show_msg = UrlShowResult(showResult);
                //保存验票信息
                await WriteTciketDetailHistoryAsync(ticketDetail, deviceListDto, showResult, jqmpass, verifiableType);
            }
            catch (Exception ex)
            {
                result.status = 0;
                result.msg = ex.Message;
                result.count = 0;
                if (string.IsNullOrEmpty(showResult.ResultMsg))
                {
                    result.audio = "ypsb.wav";
                    showResult.ResultMsg = ex.Message;
                    result.show_msg = UrlShowResult(showResult);
                }
            }
            //保存过闸记录
            await WriteGateHistoryAsync(jqmpass, deviceListDto, result, verifiableType);
            //保存至缓存中
            await SetCheckInfoCache(gateNumber, jqmpass, rdindex, result, showResult);
            return result;
        }
        #endregion

        #region 二维码验票===========================
        /// <summary>
        /// 二维码验票
        /// </summary>
        /// <param name="gateNumber"></param>
        /// <param name="jqmpass"></param>
        /// <param name="rdindex"></param>
        /// <returns></returns>
        [HttpGet,HttpPost]
        [WrapResult(WrapOnSuccess = false, WrapOnError = false)]
        public async Task<CheckResult> Scancodeopen(string gateNumber, string jqmpass, string rdindex)
        {
            VerifiableTypeEnum verifiableType = VerifiableTypeEnum.QRCode;
            CheckResult result = new CheckResult()
            {
                status = 0,
                msg = "验票成功",
                count = 1,
            };
            ShowResult showResult = new ShowResult();
            DeviceListDto deviceListDto = new DeviceListDto();
            try
            {
                //从缓存中查询验票信息
                CheckInfo checkInfo = GetCheckInfoCache(gateNumber, jqmpass, rdindex);
                if (checkInfo != null)
                    return checkInfo.checkResult;
                //查找验票设备
                deviceListDto = GetCheckDevice(gateNumber);
                //查询票据
                TicketDetail ticketDetail = GetTicketDetailByQRCode(jqmpass);
                try
                {
                    //校验票据信息
                    CheckTicketDetail(ticketDetail);
                    //校验航班信息
                    CheckSchedule(ticketDetail.Schedule);
                    result.audio = ticketDetail.ActivityDetail.TicketPrice.Ticket.AudioName;
                    showResult = getUrlByItem(ticketDetail, true, "验票成功");
                }
                catch(Exception ex)
                {
                    result.status = 0;
                    result.msg = ex.Message;
                    result.count = 0;
                    result.audio = "ypsb.wav";
                    showResult = getUrlByItem(ticketDetail, false, ex.Message);
                }
                //赋值输出结果
                result.show_msg = UrlShowResult(showResult);
                //保存验票信息
                await WriteTciketDetailHistoryAsync(ticketDetail, deviceListDto, showResult, jqmpass, verifiableType);
            }
            catch (Exception ex)
            {
                result.status = 0;
                result.msg = ex.Message;
                result.count = 0;
                if (string.IsNullOrEmpty(showResult.ResultMsg))
                {
                    result.audio = "ypsb.wav";
                    showResult.ResultMsg = ex.Message;
                    result.show_msg = UrlShowResult(showResult);
                }
            }
            //保存过闸记录
            await WriteGateHistoryAsync(jqmpass, deviceListDto, result, verifiableType);
            //保存至缓存中
            await SetCheckInfoCache(gateNumber, jqmpass, rdindex, result, showResult);
            return result;
        }

        #endregion

        #region 通用方法
        /// <summary>
        /// 查询设备信息
        /// </summary>
        /// <param name="gateNumber"></param>
        /// <returns></returns>
        protected DeviceListDto GetCheckDevice(string gateNumber)
        {
            DeviceListDto deviceListDto = new DeviceListDto();
            var (device,list) = _deviceManager.GetDeviceByCode(gateNumber);
            if (device == null)
                throw new Exception(CheckException.DeviceNull);
            if (!device.IsEnabled)
                throw new Exception(CheckException.DeviceIsEnable);
            deviceListDto = device.MapTo<DeviceListDto>();
            deviceListDto.VerifiableTypes = list; 
            return deviceListDto;
        }
        /// <summary>
        /// 根据二维码查询票据信息
        /// </summary>
        /// <param name="QRCode"></param>
        /// <returns></returns>
        protected TicketDetail GetTicketDetailByQRCode(string QRCode)
        {
            TicketDetail ticketDetail = _ticketDetaildManager.GetTicketDetailByQRCode(QRCode,"").FirstOrDefault();
            if (ticketDetail == null)
                throw new Exception(CheckException.TicketDetailNull);
            return ticketDetail;
        }
        /// <summary>
        /// 根据二维码查询票据信息
        /// </summary>
        /// <param name="QRCode"></param>
        /// <returns></returns>
        protected TicketDetail GetTicketDetailByCard(string Card)
        {
            TicketDetail ticketDetail = new TicketDetail(); 
            List<TicketDetail> ticketDetails = _ticketDetaildManager.GetTicketDetailByQRCode("",Card);
            if(ticketDetails.Count<=0)
                throw new Exception(CheckException.TicketDetailNullCard);
            if(ticketDetails.Count==1)
            {
                ticketDetail = ticketDetails.FirstOrDefault();
            }
            else
            {
                DateTime now = DateTime.Now;
                //查询否有当前正在检票的航班
                var temps = ticketDetails.Where(m => m.Schedule.CheckStartTime <= now && m.Schedule.CheckEndTime >= now).OrderBy(m => m.CreationTime).ToList();
                if(temps.Count==0)
                {
                    throw new Exception(CheckException.TicketDetailNullSchedule);
                }
                ticketDetail = temps.FirstOrDefault();
            }
            return ticketDetail;
        }
        /// <summary>
        /// 校验票据信息
        /// </summary>
        /// <param name="ticketDetail"></param>
        protected void CheckTicketDetail(TicketDetail ticketDetail)
        {
            if (ticketDetail.TicketStatus == TicketStatusEnum.TicketRefund)
                throw new Exception(CheckException.TicketDetailRefund);
            if (ticketDetail.TicketStatus == TicketStatusEnum.Used)
                throw new Exception(CheckException.TicketDetailUsed);
            switch (ticketDetail.CheckMethod)
            {
                //有效次数校验
                case CheckMethodEnum.NumberCheck:
                    if (ticketDetail.CheckingQuantity<=ticketDetail.CheckedQuantity)
                        throw new Exception(CheckException.TicketDetailNumberCheck);
                    break;
                //有效时间校验
                case CheckMethodEnum.TimeCheck:
                    if (DateTime.Now >= ticketDetail.EndDateTime)
                        throw new Exception(CheckException.TicketDetailEndDateTime);
                    if (DateTime.Now <= ticketDetail.StartDateTime)
                        throw new Exception(CheckException.TicketDetailStartDateTime);
                    break;
                //余额校验
                case CheckMethodEnum.BalanceCheck: break;
            }
        }
        /// <summary>
        /// 校验航班信息
        /// </summary>
        /// <param name="schedule"></param>
        protected void CheckSchedule(Schedule schedule)
        {
            //航线日期
            if (DateTime.Now <= schedule.CheckStartTime)
                throw new Exception(CheckException.TicketDetailStartDateTime);
            if (schedule.CheckEndTime.HasValue && DateTime.Now >= schedule.CheckEndTime)
                throw new Exception(CheckException.TicketDetailEndDateTime);
        }
        /// <summary>
        /// 保存过闸记录
        /// </summary>
        /// <param name="verifiableValue">访问信息（卡号，二维码）</param>
        /// <param name="device">设备</param>
        /// <param name="result">校验结果</param>
        /// <param name="verifiableType">访问类型</param>
        protected async Task WriteGateHistoryAsync(string verifiableValue, DeviceListDto device, CheckResult result, VerifiableTypeEnum verifiableType)
        {
            if (device == null || device.Id == Guid.Empty) return;
            var gateRecord = new GateRecord()
            {
                DeviceId = device.Id,
                VerifiableType = verifiableType,
                VerifiableValue = verifiableValue,
                ReturnState = (ReturnStateEnum)result.status,
                ReturnInfo = result.msg,
            };
            await _gateRecordManager.SaveGateRecord(gateRecord);
        }
        /// <summary>
        /// 保存验票记录
        /// </summary>
        /// <param name="ticketDetail"></param>
        /// <param name="device"></param>
        /// <param name="showResult"></param>
        /// <param name="jqmpass"></param>
        /// <param name="verifiableType"></param>
        /// <returns></returns>
        protected async Task WriteTciketDetailHistoryAsync(TicketDetail ticketDetail, DeviceListDto device,ShowResult showResult,string jqmpass, VerifiableTypeEnum verifiableType)
        {
            DateTime now = System.DateTime.Now;
            //修改票据信息
            if(showResult.IsCheck)
            {
                ticketDetail.CheckedQuantity += 1;
                ticketDetail.CheckTime = now;
                ticketDetail.TicketStatus = TicketStatusEnum.Used;
                await _ticketDetaildManager.UpdateTicketDetail(ticketDetail);
            }
            //新增验票记录
            var history = new TicketDetailHistory()
            {
                TicketId = ticketDetail.ActivityDetail.TicketPrice.TicketId,
                DeviceId = device.Id,
                DeviceName = device.DeviceName,
                TicketDetailId = ticketDetail.Id,
                TicketNo = ticketDetail.TicketNo,
                CustomerId = ticketDetail.CustomerId,
                VisitValue = jqmpass,
                ValidationType = CommonHelp.GetEnumDescription<VerifiableTypeEnum>(verifiableType),
                TicketName = ticketDetail.ActivityDetail.TicketPrice.TicketName,
                StatusCode = showResult.IsCheck ? CheckStatusEnum.CheckSuccess : CheckStatusEnum.CheckFalse,
                StatusText = showResult.ResultMsg,
                CheckDate = now.Date,
                Year = now.Year,
                Month = now.Month,
                Day = now.Day,
                Hour = now.Hour,
                Minute = now.Minute,
                YearWeek = TimeHelp.WeekOfYear(now),
                WeekDay = TimeHelp.DayOfWeek(now),
                CheckTime = now,
                CheckerId = AbpSession.UserId,
                CreationTime = now,
                CreatorUserId = AbpSession.UserId,
            };
            await _ticketDetailHistorydManager.SaveTicketDetailHistory(history);
        }
        /// <summary>
        /// 从缓存中查询验票信息
        /// </summary>
        /// <param name="gateNumber"></param>
        /// <param name="jqmpass"></param>
        /// <param name="rdindex"></param>
        /// <returns></returns>
        protected CheckInfo GetCheckInfoCache(string gateNumber, string jqmpass, string rdindex)
        {
            var cacheKey = CacheConsts.HostApi;
            var cache = _cacheManager.GetCache(cacheKey);
            var cacheValue =  cache.GetOrDefault<string, CheckInfo>(gateNumber + jqmpass);
            return cacheValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gateNumber"></param>
        /// <param name="jqmpass"></param>
        /// <param name="rdindex"></param>
        /// <param name="checkResult"></param>
        /// <param name="showResult"></param>
        /// <returns></returns>
        protected async Task SetCheckInfoCache(string gateNumber, string jqmpass, string rdindex, CheckResult checkResult, ShowResult showResult)
        {
            //将验票信息保存进缓存中
            var cacheKey = CacheConsts.HostApi;
            var cache = _cacheManager.GetCache(cacheKey);
            cache.DefaultAbsoluteExpireTime = TimeSpan.FromMinutes(1);
            await cache.SetAsync(gateNumber + jqmpass, new CheckInfo()
            {
                gateNumber = gateNumber,
                jqmpass = jqmpass,
                rdindex = rdindex,
                checkResult = checkResult,
                showResult = showResult,
            });
        }
        #endregion

        #region 私有方法
        string UrlString(string[] vs, bool isWord)
        {
            string result = string.Empty;
            string color = "#00F";
            if (isWord) color = "#F00";
            for (int i = 0; i < vs.Length; i++)
            {
                result += "<Font color=" + color + ">" + vs[i] + "</Font><br>";
            }
            return HttpUtility.HtmlEncode(result);
        }
        string UrlShowResult(ShowResult showResult)
        {
            string result = string.Empty;
            //票名称
            #region 测试
            //result += "<Font color=" + color + " size=1>Font size=1</Font>";
            //result += "<P align=left>align=left</P>";
            //result += "<P align=right size=1>align=right</P>";
            //result += "<P align=left size=7>align=left size=7</P>";
            //result += "<P align=left><Font color=" + color + " size=4>p  align=left; font size=4 align=center </Font></P>";
            //result += "<P align=right><Font color=" + color + " size=5 align=left > P align=right;font size=5 align=left </Font></P>";
            //result += "<Font color=" + color + " size=7 >一二三四五六七八九十一二三四五六七八九十size=7 </Font><br>";
            //result += "<Font color=" + color + " size=6 >一二三四五六七八九十一二三四五六七八九十size=7</Font><br>";
            //result += "<Font color=" + color + " size=3 >一二三四五六七八九十一二三四五六七八九十size=7</Font><br>";
            //result += "<Font color=" + color + " size=6 align=right > " + showResult.ItemName + "size=6 align=right </Font><br>";
            //result += "<Font color=" + color + " size=7 > " + showResult.ItemName + " </Font><br>";
            #endregion
            int posHalf = (15 - showResult.ItemName.Length) / 2;
            result += "<P align=left><Font color=#00F size=6>" + getBlanks(posHalf) + showResult.ItemName + getBlanks(posHalf) + "</Font></P>";
            string tital = getBlanks(2) + showResult.ResultMsg;
            result += "<P align=left><Font color=" + (showResult.IsCheck ? "#000" : "#F00") + " size=4>" + tital + "</Font></P>";
            tital = getBlanks(2) + "票号： " + showResult.ItemNo;
            result += "<P align=left><Font color=#000 size=4>" + tital + "</Font></P>";
            tital = getBlanks(2) + "可验票数： " + showResult.CheckingQuantity;
            result += "<P align=left><Font color=#000 size=4>" + tital + "</Font></P>";
            tital = getBlanks(2) + "已验票数： " + showResult.CheckedQuantity;
            result += "<P align=left><Font color=#000 size=4>" + tital + "</Font></P>";
            tital = getBlanks(2) + "有效日期： " + showResult.StartDateTime + " 至 " + showResult.EndDateTime;
            result += "<P align=left><Font color=#000 size=4>" + tital + "</Font></P>";
            return HttpUtility.HtmlEncode(result);
        }
        ShowResult getUrlByItem(TicketDetail ticketDetail, bool isCheck, string Msg)
        {
            return new ShowResult()
            {
                IsCheck = isCheck,
                ResultMsg = Msg,
                ItemName = ticketDetail.ActivityDetail.TicketPrice.TicketName,
                ItemNo = ticketDetail.TicketNo,
                CheckedQuantity = ticketDetail.CheckedQuantity.ToString(),
                CheckingQuantity = ticketDetail.CheckingQuantity.ToString(),
                StartDateTime = ticketDetail.StartDateTime.ToString("yyyy-MM-dd"),
                EndDateTime = ticketDetail.EndDateTime.ToString("yyyy-MM-dd")
            };
        }
        /// <summary>
        /// 获取对应长度的空格
        /// </summary>
        /// <param name="k"></param>
        /// <returns></returns>
        string getBlanks(int k)
        {
            string result = "";
            while (k > 0)
            {
                result += "&nbsp;";
                k--;
            }
            return result;
        }

        #endregion
    }
}
